#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <mutex>
#include <atomic>
#include <thread>

#include "common/net_utils.h"
#include "net_tools.h"

using namespace std;

#define SERVER_HOST "127.0.0.1"
#define SERVER_PORT 6666

//#define CREATE_TASK CoApi::create

atomic<int>  g_ends(0);

void normal_send_test(int argc, char** argv);
void normal_recv_test(int argc, char** argv);
void pressure_test(int argc, char** argv);

int main(int argc, char** argv)
{
/*
    LogDefaulter::set_log_level(LOG_LEVEL_INFO);
    LogDefaulter::set_logger([](const string& message) {
        printf("socket_test|%s\n", message.c_str());
        fflush(stdout);
    });
*/
	NetTools::ignore_sig_pipe();

   // pressure_test(argc, argv);

    normal_send_test(argc, argv);

   // normal_recv_test(argc, argv);

    PRINTF("main end");

    return 0;
}

void normal_send_test(int argc, char** argv)
{
    string server_host = SERVER_HOST;
    int    server_port = SERVER_PORT;

    if (argc >= 2) {
        server_host = argv[2];
    }
    if (argc >= 3) {
        server_port = atoi(argv[3]);
    }

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    assert(fd >= 0);

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port   = htons(SERVER_PORT);
    
    int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    assert(ret == 0);
    printf("connect server succeed, host:%s, port:%d\n", server_host.c_str(), server_port);

    const char* SEND_TEXT = "hello_world";
/*
    ret = NetTools::send_packet(fd, SEND_TEXT);
    assert(ret > 0);
    printf("send text to server succeed, text:%s\n", SEND_TEXT);

    close(fd);
*/

    atomic<int> i(0);

    thread([&i, fd] {
        int last_value = i.load();
        while (1) {
            usleep(100 * 1000);
            if (last_value == i.load()) {
                break ;
            }
        }
        printf("socket close ready, fd:%d\n", fd);
        close(fd);
        printf("socket close pass, fd:%d\n", fd);
    }).detach();

    while (1) {
        ret = NetTools::send_packet(fd, SEND_TEXT);
        assert(ret > 0);
        printf("send text to server succeed, i:%d, text:%s\n", i++, SEND_TEXT);
    }

    while (1);
}

void normal_recv_test(int argc, char** argv)
{
    string server_host = SERVER_HOST;
    int    server_port = SERVER_PORT;

    if (argc >= 2) {
        server_host = argv[2];
    }
    if (argc >= 3) {
        server_port = atoi(argv[3]);
    }

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    assert(fd >= 0);

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port   = htons(SERVER_PORT);
    
    int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    assert(ret == 0);
    printf("connect server succeed, host:%s, port:%d\n", server_host.c_str(), server_port);

    const char* SEND_TEXT = "hello_world";

    ret = NetTools::send_packet(fd, SEND_TEXT);
    assert(ret > 0);
    printf("send text to server succeed, fd:%d, text:%s\n", fd, SEND_TEXT);

    NetTools::create_task([fd] {
        string content;
        printf("recv_packet ready, fd:%d\n", fd);
        int ret = NetTools::recv_packet(fd, content);
        printf("recv_packet pass, fd:%d, ret:%d\n", fd, ret);
        assert(ret > 0);
        printf("recv text to server succeed, fd:%d, text:%s\n", fd, content.c_str());
    });

    printf("close socket ready, fd:%d\n", fd);
    usleep(100 * 1000);
    close(fd);
    printf("close socket pass, fd:%d\n", fd);

    while (1);
}

void pressure_test(int argc, char** argv)
{
    atomic<long> g_counter(0);

    CoClientCb cb;
    cb.connect_cb = [](int fd, bool is_ok) {
		PRINTF("##### cb #####|client connect %s, fd:%d", is_ok ? "succeed" : "fail", fd);
	};
    cb.send_cb = [](int fd, const string& buffer, bool is_ok) {
       // PRINTF("##### cb #####|client send %s, fd:%d, buffer:%s", is_ok ? "succeed" : "fail", fd, buffer.c_str());
    };
    cb.recv_cb = [&g_counter](int fd, const string& buffer, bool is_ok) {
       // PRINTF("##### cb #####|client recv %s, fd:%d, buffer:%s", is_ok ? "succeed" : "fail", fd, buffer.c_str());
		g_counter++;
    };
    cb.close_cb = [](int fd) {
        ;
    }; 

    int client_count = 1;
    string server_host = SERVER_HOST;
    int    server_port = SERVER_PORT;

    if (argc >= 2) {
        client_count = atoi(argv[1]);
    }
    if (argc >= 3) {
        server_host = argv[2];
    }
    if (argc >= 4) {
        server_port = atoi(argv[3]);
    }

    PRINTF(
        "host:%s, port:%d, client_count:%d", 
        server_host.c_str(), 
        server_port, 
        client_count
    );

    atomic<int> coroutines(0);

    if (argc == 2) {
        client_count = atoi(argv[1]); 
    }
    PRINTF("client_count:%d", client_count)

    for (int i = 0; i < client_count; i++) {
        NetTools::create_task([&coroutines, server_host, server_port, cb] {
            coroutines++; 
            NetTools::echo_sender(server_host, server_port, -1, cb);
            g_ends++;
        });
    }

	long last_counter = 0;
	while (g_ends.load() != client_count) {
		usleep(1000 * 1000);
		long now_counter = g_counter.load();
        PRINTF("++++++++++ qps:%ld, coroutine_count:%d", now_counter - last_counter, coroutines.load());
		last_counter = now_counter;
	}
}

